# coding=utf-8
# author=haishan09@gmail.com
import json
import datetime
import calendar
from django.forms import TextInput, Select, SelectMultiple
from django.utils.html import format_html
from django.forms.utils import flatatt
from django.utils.encoding import force_text


class ComponetUpdateMixin(object):

    def apply_components_change(self, attrs):
        templ = self.templ
        javascript = self.javascript
        for k, v in self.cutship.dconf.items():
            try:
                func = getattr(self, 'set_' + k)
            except AttributeError:
                pass
            else:
                attrs, templ, javascript = func(attrs, templ, javascript)
        return attrs, templ, javascript

    @property
    def child_cuts(self):
        return []


class BaseTextInput(TextInput, ComponetUpdateMixin):

    def __init__(self, cutship, *args, **kwargs):
        self.cutship = cutship
        self.dconf = self.cutship.dconf
        super(BaseTextInput, self).__init__(*args, **kwargs)

    def set_visible(self, attrs, templ, javascript):
        if not self.dconf.get('visible'):
            templ = templ.replace('inline-table', 'none')
        return attrs, templ, javascript 

    def set_editable(self, attrs, templ, javascript):
        if not self.dconf.get('editable'):
            attrs.update({'readonly': 'readonly'})
        return attrs, templ, javascript 


class BaseSelect(Select, ComponetUpdateMixin):

    def __init__(self, cutship, *args, **kwargs):
        self.cutship = cutship
        self.dconf = self.cutship.dconf
        super(BaseSelect, self).__init__(*args, **kwargs)

    def set_visible(self, attrs, templ, javascript):
        ele = attrs['id']
        if not self.dconf.get('visible'):
            javascript += '''
                <script type="text/javascript">
                    $("#%s").next().hide()
                </script>''' % ele
        return attrs, templ, javascript 

    def set_editable(self, attrs, templ, javascript):
        if not self.dconf.get('editable'):
            attrs.update({'readonly': 'readonly'})
        return attrs, templ, javascript 


class BaseSelectMultiple(SelectMultiple, ComponetUpdateMixin):

    def __init__(self, cutship, *args, **kwargs):
        self.cutship = cutship
        self.dconf = self.cutship.dconf
        super(BaseSelectMultiple, self).__init__(*args, **kwargs)

    def set_visible(self, attrs, templ, javascript):
        ele = attrs['id']
        if not self.dconf.get('visible'):
            javascript += '''
                <script type="text/javascript">
                    $("#%s").next().hide()
                </script>''' % ele
        return attrs, templ, javascript 

    def set_editable(self, attrs, templ, javascript):
        if not self.dconf.get('editable'):
            attrs.update({'readonly': 'readonly'})
        return attrs, templ, javascript 


class RangeDateWidget(BaseTextInput):

    templ = '''
        <div class="input-group btn-group" style="display:inline-table">
            <span class="input-group-addon"><i class="fa fa-calendar bigger-110"></i></span>
            <input class="datepicker form-control" type="text" {} />
        </div>'''

    javascript = '''
        <script type="text/javascript">
            $('label[for="{ele}"]').hide();
            var startDate = "{start}";
            var endDate = "{end}";
            function cb(start, end) {{
                $("#{ele}").html(start.format("YYYYMMDD") + "-" + end.format("YYYYMMDD"));
            }}'''
    rangedate = '''
            $("#{ele}").daterangepicker({{
                startDate: startDate,
                endDate: endDate,
                ranges: {{
                   "今日": [moment().subtract(0, "days"), moment().subtract(0, "days")],
                   "昨日": [moment().subtract(1, "days"), moment().subtract(1, "days")],
                   "前3日": [moment().subtract(3, "days"), moment().subtract(1, "days")],
                   "前7日": [moment().subtract(7, "days"), moment().subtract(1, "days")],
                   "前15日": [moment().subtract(15, "days"), moment().subtract(1, "days")],
                   "前30日": [moment().subtract(30, "days"), moment().subtract(1, "days")],
                   "本周": [moment().startOf("week").subtract(-1, "days"), moment().endOf("week").subtract(-1, "days")],
                   "上周": [moment().subtract(1, "week").startOf("week").subtract(-1, "days"), moment().subtract(1, "week").endOf("week").subtract(-1, "days")],
                   "本月": [moment().startOf("month"), moment().endOf("month")],
                   "上月": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
                }},
                locale: {{
                    format: "YYYYMMDD",
                    firstDay: 1,
                    "applyLabel": "确定",
                    "cancelLabel": "取消",
                    "customRangeLabel": "自定义"
                }}     
            }}, cb);
        </script>'''

    singledate = '''
            $("#{ele}").daterangepicker({{
                startDate: startDate,
                endDate: endDate,
                singleDatePicker: true,
                showDropdowns: true,
                locale: {{
                    format: "YYYYMMDD",
                }}
            }});
        </script>'''

    def set_range(self, attrs, templ, javascript):
        if self.dconf.get('range'):
            return attrs, templ, javascript + self.rangedate
        return attrs, templ, javascript + self.singledate 

    def render(self, name, value, attrs=None):
        now = datetime.datetime.now()
        monthrange = calendar.monthrange(now.year, now.month)[1]
        day_of_week =  now.isoweekday()
        day_of_month = now.day
        choices = { 
            '1': (now, now),
            '2': (now - datetime.timedelta(days=1), now - datetime.timedelta(days=1)),
            '3': (now - datetime.timedelta(days=3), now - datetime.timedelta(days=1)),
            '4': (now - datetime.timedelta(days=7), now - datetime.timedelta(days=1)),
            '5': (now - datetime.timedelta(days=15), now - datetime.timedelta(days=1)),
            '6': (now - datetime.timedelta(days=30), now - datetime.timedelta(days=1)),
            '7': (now - datetime.timedelta(days=day_of_week - 1), 
                  now + datetime.timedelta(days=7 - day_of_week)),
            '8': (now - datetime.timedelta(days=day_of_week + 6), 
                  now + datetime.timedelta(days=0 - day_of_week)),
            '9': (now - datetime.timedelta(days=day_of_month - 1),
                  now + datetime.timedelta(days=monthrange - day_of_month)),
            '10': ((now - datetime.timedelta(days=day_of_month)).replace(day=1), 
                   now - datetime.timedelta(days=day_of_month)),
        }
        attrs, templ, javascript = self.apply_components_change(attrs)
        start, end = choices.get(value or '2')
        final_attrs = self.build_attrs(attrs, name=name)
        if value:
            final_attrs['value'] = force_text(start.strftime('%m/%d/%Y'))
        ele = final_attrs['id']
        javascript = javascript.format(**{'ele': ele, 'start': start, 'end': end})
        return format_html(templ, flatatt(final_attrs)) + javascript

    class Media:
        css = {
            'all': ('css/daterangepicker.css', ),
        }
        js = ('js/date-time/moment.js', 'js/date-time/daterangepicker.js')


class SearchBoxWidget(BaseTextInput):

    templ = '''
        <div class="input-group btn-group" style="display:inline-table">
            <span class="input-icon">
                <input class="form-control multiselect-search" 
                    placeholder="{1}" {0}>
                <i class="ace-icon fa fa-search nav-search-icon"></i>
            </span>
        </div>'''

    javascript = '''
        <script type="text/javascript">
            $('label[for="%s"]').hide();
            var sug_keys = {};
            $("#%s").tag({
                placeholder: $("#%s").attr("placeholder"),
                autocompleteOnComma: true,
                source: function(query, process) {
                    $.ajax({
                        url: "/dimension/search/" + "%s" + "/" + encodeURIComponent(query)
                    }).done(function(result_items) {
                        var sug_vals = [];
                        $.each(result_items, function(i, item) {
                            sug_keys[item.split("###")[1]] = item.split("###")[0];
                            sug_vals.push(item.split("###")[1]);
                        });
                        process(sug_vals);
                    });
                }
            }); 
            $("#%s").on("added removed", function(e, val){
                var tag_obj = $("#%s").data("tag");
                var tag_vals = tag_obj.values;
                var tag_keys = [];
                $.each(tag_vals, function(i, tag) {
                    tag_keys.push(sug_keys[tag]);
                });
                $("#%s").val(tag_keys.join(";"));
            });
        </script>'''

    def render(self, name, value, attrs=None):
        attrs, templ, javascript = self.apply_components_change(attrs)
        value = value or ''
        final_attrs = self.build_attrs(attrs, name=name)
        if value:
            final_attrs['value'] = force_text(value)
        ele = final_attrs['id']
        name = final_attrs['name']
        javascript = javascript % (ele, ele, ele, name, ele, ele, ele)
        return format_html(templ, flatatt(final_attrs), self.cutship.cut.label) + javascript

    class Media:
        js = ('js/ace/ace.searchbox-autocomplete.js', 
              'js/ace/elements.typeahead.js',
              'js/bootstrap-tag.js')


class SelectWidget(BaseSelect):
    
    templ = ''

    javascript = '''
        <script type="text/javascript">
            $('#{ele}').multiselect({{
                maxHeight: 600,
                enableFiltering: true,
                includeSelectAllOption: true,
                buttonClass: "btn btn-white btn-primary",
		        numberDisplayed: 3,
                buttonText: function(options, select) {{
                    var labelname = select.prev().text().replace(":", "");
                    if (options.length === 0) {{
                        return '<span class="multiselect-selected-text">' + labelname + ': ALL</span><i class="ace-icon fa fa-angle-down icon-on-right"></i>';
                    }}
                    else if (options.length > 4) {{
                        var context = options.length + " options selected";
                        return '<span class="multiselect-selected-text">' + context + '</span><i class="ace-icon fa fa-angle-down icon-on-right"></i>';
                    }}
                    else {{
                        var labels = [];
                        options.each(function() {{
                            if ($(this).attr("label") !== undefined) {{
                                labels.push($(this).attr("label"));
                            }}
                            else {{
                                labels.push($(this).html());
                            }}
                        }});
                        var context = labelname + ": " + labels.join(", ") + "";
                        return '<span class="multiselect-selected-text">' + context  + '</span><i class="ace-icon fa fa-angle-down icon-on-right"></i>';
                    }}
                }},
                templates: {{
                   button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"></button>',
                   ul: '<ul class="multiselect-container dropdown-menu"></ul>',
                   filter: '<li class="multiselect-item filter"><div class="input-group"><span class="input-group-addon"><i class="fa fa-search"></i></span><input class="form-control multiselect-search" type="text"></div></li>',
                   filterClearBtn: '<span class="input-group-btn"><button class="btn btn-default btn-white btn-grey multiselect-clear-filter" type="button"><i class="fa fa-times-circle red2"></i></button></span>',
                   li: '<li><a href="javascript:void(0);"><label></label></a></li>',
                   divider: '<li class="multiselect-item divider"></li>',
                   liGroup: '<li class="multiselect-item group"><label class="multiselect-group"></label></li>'
                }}
            }});
            $('label[for="{ele}"]').hide();
            function get_{ele}_choices() {{
                $.ajax({{
                    type: 'GET',
                    url: '{url}',
                    data: $('#cuts_form').serialize(),
                    dataType: 'JSON',
                    success: function(data) {{
                        data.splice(0, 0, {{value: '', label: 'ALL'}});
                        $('#{ele}').multiselect('dataprovider', data)
                        $('#{ele}').val({value});
                        $('#{ele}').multiselect('rebuild');

                    }}
                }});
            }};
            get_{ele}_choices();
            document.getElementById('{ele}').onchange = function() {{
                {onchange}
            }}
        </script>''' 

    def render(self, name, value, attrs=None):
        onchange = ''
        for cut in self.child_cuts:
            onchange += 'get_id_{0}_choices();'.format(cut.name)
        attrs, templ, javascript = self.apply_components_change(attrs)
        final_attrs = self.build_attrs(attrs, name=name)
        ele = final_attrs['id']
        html = super(SelectWidget, self).render(name, value, attrs)
        return html + javascript.format(**{'ele': ele, 
                                           'url': self.cutship.cut.choices_uri, 
                                           'value': value,
                                           'onchange': onchange})


    class Media:
        css = {'all': ('css/bootstrap-multiselect.css', )}
        js = ('js/bootstrap-multiselect.js', )


class SelectMultipleWidget(BaseSelectMultiple):

    templ = ''

    javascript = '''
        <script type="text/javascript">
            $('#{ele}').multiselect({{
                maxHeight: 600,
                enableFiltering: true,
                includeSelectAllOption: true,
                buttonClass: "btn btn-white btn-primary",
                buttonText: function(options, select) {{
                    var labelname = select.prev().text().replace(":", "");
                    if (options.length === 0) {{
                        return '<span class="multiselect-selected-text">' + labelname + ': ALL</span><i class="ace-icon fa fa-angle-down icon-on-right"></i>';
                    }}
                    else if (options.length > 4) {{
                        var context = options.length + " options selected";
                        return '<span class="multiselect-selected-text">' + context + '</span><i class="ace-icon fa fa-angle-down icon-on-right"></i>';
                    }}
                    else {{
                        var labels = [];
                        options.each(function() {{
                            if ($(this).attr("label") !== undefined) {{
                                labels.push($(this).attr("label"));
                            }}
                            else {{
                                labels.push($(this).html());
                            }}
                        }});
                        var context = labelname + ": " + labels.join(", ") + "";
                        return '<span class="multiselect-selected-text">' + context  + '</span><i class="ace-icon fa fa-angle-down icon-on-right"></i>';
                    }}
                }},
                templates: {{
                   button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"></button>',
                   ul: '<ul class="multiselect-container dropdown-menu"></ul>',
                   filter: '<li class="multiselect-item filter"><div class="input-group"><span class="input-group-addon"><i class="fa fa-search"></i></span><input class="form-control multiselect-search" type="text"></div></li>',
                   filterClearBtn: '<span class="input-group-btn"><button class="btn btn-default btn-white btn-grey multiselect-clear-filter" type="button"><i class="fa fa-times-circle red2"></i></button></span>',
                   li: '<li><a href="javascript:void(0);"><label></label></a></li>',
                   divider: '<li class="multiselect-item divider"></li>',
                   liGroup: '<li class="multiselect-item group"><label class="multiselect-group"></label></li>'
                }}
            }});
            $('label[for="{ele}"]').hide();
            function get_{ele}_choices() {{
                $.ajax({{
                    type: 'GET',
                    url: '{url}',
                    data: $('#cuts_form').serialize(),
                    dataType: 'JSON',
                    success: function(data) {{
                        $('#{ele}').multiselect('dataprovider', data)
                        $('#{ele}').val({value});
                        $('#{ele}').multiselect('rebuild');

                    }}
                }});
            }};
            get_{ele}_choices();
            document.getElementById('{ele}').onchange = function() {{
                {onchange}
            }}
        </script>''' 

    def render(self, name, value, attrs=None):
        onchange = ''
        for cut in self.child_cuts:
            onchange += 'get_id_{0}_choices();'.format(cut.name)
        attrs, templ, javascript = self.apply_components_change(attrs)
        final_attrs = self.build_attrs(attrs, name=name)
        ele = final_attrs['id']
        html = super(SelectMultipleWidget, self).render(name, value, attrs)
        return html + javascript.format(**{'ele': ele, 
                                           'url': self.cutship.cut.choices_uri, 
                                           'value': json.dumps(value),
                                           'onchange': onchange})

    class Media:
        css = {'all': ('css/bootstrap-multiselect.css', )}
        js = ('js/bootstrap-multiselect.js', )

class BtnGroup(BaseSelect):
    
    templ = '''
        <div data-toggle="buttons" class="input-group btn-group" style="display:inline-table">
            {0}
        </div>
    '''

    javascript = '''
        <script type="text/javascript">
            $('label[for="%s"]').hide();
        </script>'''

    def render(self, name, value, attrs=None):
        choices_templ = ''
        active_choice_templ = '''
            <label class="btn btn-white btn-sm btn-primary active" style="line-height: 32px">
                <span class="bigger-110">%s</span>
                <input type="radio" value="%s" checked {0}/>
            </label>
        '''
        choice_templ = '''
            <label class="btn btn-white btn-sm btn-primary" style="line-height: 32px">
                <span class="bigger-110">%s</span>
                <input type="radio" value="%s" {0}/>
            </label>
        '''
        for choice in self.cut.choices:
            if str(choice[0]) == str(value):
                choices_templ += active_choice_templ % (choice[1], choice[0])
            else:
                choices_templ += choice_templ % (choice[1], choice[0])
        attrs, templ, javascript = self.apply_components_change(attrs)
        final_attrs = self.build_attrs(attrs, name=name)
        ele = final_attrs['id']
        return format_html(templ.format(choices_templ), flatatt(final_attrs)) + javascript % ele

class InputWidget(BaseTextInput):

    templ = '''
        <div class="input-group btn-group" style="display:inline-table">
            <input class="form-control" placeholder="{1}" {0}>
        </div>
    '''

    javascript = '''
        <script type="text/javascript">
            $('label[for="%s"]').hide();
        </script>'''

    def render(self, name, value, attrs=None):
        attrs, templ, javascript = self.apply_components_change(attrs)
        final_attrs = self.build_attrs(attrs, name=name)
        ele = final_attrs['id']
        return format_html(templ, flatatt(final_attrs), self.cutship.cut.label) + javascript % ele
